; 
; Window Spy for AHKv2
;

#无托盘图标
#单例模式 Ignore
设置工作目录 内_脚本目录
坐标模式 "Pixel", "Screen"

Global 主窗口

WinSpyGui()

WinSpyGui() {
	未冻结提示 := "(按住 Ctrl 或 Shift 来冻结窗口)"
	冻结提示 := "(已停止更新)"
	鼠标控件文本 := "鼠标指向控件"
	焦点控件文本 := "焦点指向控件"
	
	主窗口 := 界面.新建("置顶 可调整 最小尺寸 +像素缩放","窗口探针"), 主窗口句柄 := 主窗口.句柄
	主窗口.监听事件("关闭","关闭主窗口")
	主窗口.监听事件("大小","主窗口缩放")
	
	主窗口.添加("文本",,"窗口标题 类 和进程:")
	主窗口.添加("多选框","yp xp+200 w120 Right vCtrl_FollowMouse","Follow Mouse").值 := 1
	主窗口.添加("编辑框","xm w320 r5 ReadOnly -Wrap vCtrl_Title")
	主窗口.添加("文本",,"Mouse Position")
	主窗口.添加("编辑框","w320 r4 ReadOnly vCtrl_MousePos")
	主窗口.添加("文本","w320 vCtrl_CtrlLabel",焦点控件文本 ":")
	主窗口.添加("编辑框","w320 r4 ReadOnly vCtrl_Ctrl")
	主窗口.添加("文本",,"Active Window Postition:")
	主窗口.添加("编辑框","w320 r2 ReadOnly vCtrl_Pos")
	主窗口.添加("文本",,"Status Bar Text:")
	主窗口.添加("编辑框","w320 r2 ReadOnly vCtrl_SBText")
	主窗口.添加("多选框","vCtrl_IsSlow","Slow TitleMatchMode")
	主窗口.添加("文本",,"Visible Text:")
	主窗口.添加("编辑框","w320 r2 ReadOnly vCtrl_VisText")
	主窗口.添加("文本",,"All Text:")
	主窗口.添加("编辑框","w320 r2 ReadOnly vCtrl_AllText")
	主窗口.添加("文本","w320 r1 vCtrl_Freeze",未冻结提示)
	
	主窗口.Show("NoActivate")
	GetClientSize(主窗口句柄, temp)
	
	horzMargin := temp*96//内_屏幕像素密度 - 320
	主窗口.horzMargin := horzMargin
	
	主窗口.未冻结提示 := 未冻结提示, 主窗口.冻结提示 := 冻结提示
	主窗口.鼠标控件文本 := 鼠标控件文本, 主窗口.焦点控件文本 := 焦点控件文本
	
	设置定时器 "Update", 250
}

主窗口缩放(GuiObj, MinMax, Width, Height) {
	horzMargin := 主窗口.horzMargin
	如果 !horzMargin
		返回
	
	设置定时器 "Update", (MinMax=0)?250:0 ; 挂起 updates on minimize
	
	ctrlW := Width - horzMargin
	list := "Title,MousePos,Ctrl,Pos,SBText,VisText,AllText,Freeze"
	循环 Parse list, ","
		ctl := 主窗口["Ctrl_" 内_循环属性], ctl.Move(,,ctrlW)
}

关闭主窗口(GuiObj) {
	退出应用
}

Update() { ; timer, no params
	主窗口句柄 := 主窗口.句柄
	未冻结提示 := 主窗口.未冻结提示, 冻结提示 := 主窗口.冻结提示
	鼠标控件文本 := 主窗口.鼠标控件文本, 焦点控件文本 := 主窗口.焦点控件文本
	
	Ctrl_FollowMouse := 主窗口["Ctrl_FollowMouse"].值
	坐标模式 "Mouse", "Screen"
	获取鼠标位置 msX, msY, msWin, msCtrl, 2 ; get ClassNN and hWindow
	actWin := 窗口存在("A")
	
	如果 (Ctrl_FollowMouse) {
		curWin := msWin, curCtrl := msCtrl
		窗口存在("ahk_id " curWin) ; updating LastWindowFound?
	} 否则 {
		curWin := actWin
		curCtrl := 获取焦点控件() ; get focused 控制 hwnd from active win
	}
	curCtrlClassNN := ""
	尝试 curCtrlClassNN := 获取控件类名编号(curCtrl)
	
	t1 := 获取窗口标题(), t2 := 获取窗口类名()
	如果 (curWin = 主窗口句柄 || t2 = "MultitaskingViewFrame") { ; Our Gui || 换挡-tab
		UpdateText("Ctrl_Freeze", 冻结提示)
		返回
	}
	
	UpdateText("Ctrl_Freeze", 未冻结提示)
	t3 := 获取窗口进程名称(), t4 := 获取窗口进程编号()
	
	UpdateText("Ctrl_Title", t1 "`nahk_class " t2 "`nahk_exe " t3 "`nahk_pid " t4 "`nahk_id " curWin)
	坐标模式 "Mouse", "Window"
	获取鼠标位置 mrX, mrY
	坐标模式 "Mouse", "Client"
	获取鼠标位置 mcX, mcY
	mClr := 获取像素颜色(msX,msY,"RGB")
	mClr := 截取字符串(mClr, 3)
	
	UpdateText("Ctrl_MousePos", "Screen:`t" msX ", " msY " (less often used)`nWindow:`t" mrX ", " mrY " (default)`nClient:`t" mcX ", " mcY " (recommended)"
		. "`nColor:`t" mClr " (Red=" 截取字符串(mClr, 1, 2) " Green=" 截取字符串(mClr, 3, 2) " Blue=" 截取字符串(mClr, 5) ")")
	
	UpdateText("Ctrl_CtrlLabel", (Ctrl_FollowMouse ? 鼠标控件文本 : 焦点控件文本) ":")
	
	如果 (curCtrl) {
		ctrlTxt := 获取控件文本(curCtrl)
		cText := "ClassNN:`t" curCtrlClassNN "`nText:`t" textMangle(ctrlTxt)
		获取控件位置 cX, cY, cW, cH, curCtrl
		cText .= "`n`tx: " cX "`ty: " cY "`tw: " cW "`th: " cH
		WinToClient(curWin, cX, cY)
		GetClientSize(curCtrl, cW, cH)
		cText .= "`nClient:`tx: " cX "`ty: " cY "`tw: " cW "`th: " cH
	} 否则
		cText := ""
	
	UpdateText("Ctrl_Ctrl", cText)
	wX := "", wY := "", wW := "", wH := ""
	尝试 获取窗口位置 wX, wY, wW, wH
	GetClientSize(curWin, wcW, wcH)
	
	UpdateText("Ctrl_Pos", "`tx: " wX "`ty: " wY "`tw: " wW "`th: " wH "`nClient:`tx: 0`ty: 0`tw: " wcW "`th: " wcH)
	sbTxt := ""
	
	循环 {
		ovi := ""
		尝试 ovi := 获取状态栏文本(内_循环次数)
		如果 (ovi = "")
			跳出
		sbTxt .= "(" 内_循环次数 "):`t" textMangle(ovi) "`n"
	}
	
	sbTxt := 截取字符串(sbTxt,1,-1) ; StringTrimRight, sbTxt, sbTxt, 1
	UpdateText("Ctrl_SBText", sbTxt)
	bSlow := 主窗口["Ctrl_IsSlow"].值 ; GuiControlGet, bSlow,, Ctrl_IsSlow
	
	如果 (bSlow) {
		检测隐藏文本 False
		ovVisText := 获取窗口文本() ; 获取窗口文本, ovVisText
		检测隐藏文本 True
		ovAllText := 获取窗口文本() ; 获取窗口文本, ovAllText
	} 否则 {
		ovVisText := WinGetTextFast(false)
		ovAllText := WinGetTextFast(true)
	}
	
	UpdateText("Ctrl_VisText", ovVisText)
	UpdateText("Ctrl_AllText", ovAllText)
}

; ===========================================================================================
; 获取窗口文本 ALWAYS uses the "fast" mode - TitleMatchMode only affects to retrieve the text
; of each 控制. WinText/ExcludeText parameters.  In Slow mode, GetWindowText() is used.
; ===========================================================================================
WinGetTextFast(detect_hidden) {	
	controls := 获取窗口控件编号() ; "ahk_id " curWin
	
	如果 (类型(controls) = "Array") {
		静态 WINDOW_TEXT_SIZE := 32767 ; Defined in AutoHotkey source.
		
		buf := 创建缓冲区(WINDOW_TEXT_SIZE * 1,0) ; 创建缓冲区(WINDOW_TEXT_SIZE * (A_IsUnicode ? 2 : 1),0)
		
		text := ""
		
		循环 controls.Length {
			hCtl := controls[内_循环次数]
			如果 !detect_hidden && !动态库调用("IsWindowVisible", "ptr", hCtl)
				跳过
			如果 !动态库调用("GetWindowText", "ptr", hCtl, "Ptr", buf.ptr, "int", WINDOW_TEXT_SIZE)
				跳过
			; text .= buf "`r`n"
			text .= 获取字符串(buf)
		}
		返回 text
	} 否则
		返回 ""
}

; ===========================================================================================
; Unlike using a pure GuiControl, this function causes the text of the
; controls to be updated only when the text has changed, preventing periodic
; flickering (especially on older systems).
; ===========================================================================================
UpdateText(vCtl, NewText) {
	静态 OldText := {}
	ctl := 主窗口[vCtl], hCtl := 整数(ctl.句柄)
    ; 信息框 hCtl
	
	如果 (!oldText.包含属性(hCtl) Or OldText.%hCtl% != NewText) {
		ctl.值 := NewText
		OldText.%hCtl% := NewText
	}
}

GetClientSize(hWnd, ByRef w := "", ByRef h := "") {
	rect := 创建缓冲区(16,0)
	; VarSetCapacity(rect, 16)
	动态库调用("GetClientRect", "ptr", hWnd, "ptr", rect.ptr)
	w := 获取数值(rect, 8, "int"), h := 获取数值(rect, 12, "int")
}

WinToClient(hWnd, ByRef x, ByRef y) {
    获取窗口位置 wX, wY,,, "ahk_id " hWnd
	如果 (wX != "" And wY != "") {
		x += wX, y += wY
		pt := 创建缓冲区(8,0)
		; VarSetCapacity(pt, 8)
		
		置数值 "Int", x, pt
		置数值 "Int", y, pt, 4
		
		; 置数值(y, 置数值(x, pt, "int"), "int")
		
		如果 !动态库调用("ScreenToClient", "ptr", hWnd, "ptr", pt.ptr)
			返回 false
		x := 获取数值(pt, 0, "int"), y := 获取数值(pt, 4, "int")
		返回 true
	} 否则
		返回 false
    
}

textMangle(x) {
	elli := false
	如果 (pos := 查找(x, "`n"))
		x := 截取字符串(x, 1, pos-1), elli := true
	否则 如果 (字符串长度(x) > 40)
		x := 截取字符串(x,1,40), elli := true
	如果 elli
		x .= " (...)"
	返回 x
}

~*Ctrl::
~*上档::
{
	冻结提示 := 主窗口.冻结提示
	设置定时器 "Update", 0
	UpdateText("Ctrl_Freeze", 冻结提示)
}

~*Ctrl up::
~*上档 up::设置定时器 "Update", 250